package analysis

import (
	"fmt"
	mqtt "github.com/eclipse/paho.mqtt.golang"
	"github.com/goburrow/modbus"
	"math"
	"strconv"
	"strings"
	"sync"
	"time"
)

var (
	DATA_TYPE_INT16  = "INT16"
	DATA_TYPE_UINT16 = "UINT16"
	DATA_TYPE_INT32  = "INT32"
	DATA_TYPE_UINT32 = "UINT32"
)

const (
	LITTLE_ENDIAN = iota
	BIG_ENDIAN
)

type DevInfo struct {
	InterType       string `json:"inter_type"`
	InterName       string `json:"inter_name"`
	BaudRate        int    `json:"baud_rate"`
	StopBit         int    `json:"stop_bit"`
	ParityBit       int    `json:"parity_bit"`
	DataBit         int    `json:"data_bit"`
	MonitorMode     int    `json:"monitor_mode"`
	Bridge          string `json:"bridge"`
	FrameTimerout   int    `json:"frame_timerout"`
	Address         string `json:"address"`
	Port            string `json:"port"`
	ProtocolType    string `json:"protocol_type"`
	DevName         string `json:"dev_name"`
	DevTimeout      int    `json:"dev_timeout"`
	DevId           int    `json:"dev_id"`
	DevType         string `json:"dev_type"`
	ModbusClient    modbus.Client
	HandleClient    *modbus.TCPClientHandler
	HandleRtuClient *modbus.RTUClientHandler
	MqttClient      mqtt.Client
}

func DevAnalysis_strings(info []string) DevInfo {
	devInfo := DevInfo{}
	for i, s := range info {
		switch i {
		case 0: //接口类型
			devInfo.InterType = s
			break
		case 1: //接口编号
			devInfo.InterName = s
			break
		case 2: //波特率
			atoi, err := strconv.Atoi(s)
			if err != nil {
				fmt.Println(err)
			} else {
				devInfo.BaudRate = atoi
			}

			break
		case 3: //停止位
			atoi, err := strconv.Atoi(s)
			if err != nil {
				fmt.Println(err)
			} else {
				devInfo.StopBit = atoi
			}

			break
		case 4: //校验位
			atoi, err := strconv.Atoi(s)
			if err != nil {
				fmt.Println(err)
			} else {
				devInfo.ParityBit = atoi
			}
			break
		case 5: //数据位
			atoi, err := strconv.Atoi(s)
			if err != nil {
				fmt.Println(err)
			} else {
				devInfo.DataBit = atoi
			}
			break
		case 6: //监听模式
			atoi, err := strconv.Atoi(s)
			if err != nil {
				fmt.Println(err)
			} else {
				devInfo.MonitorMode = atoi
			}

			break
		case 7: //桥接模式
			devInfo.Bridge = s
			break
		case 8: //超时时间
			atoi, err := strconv.Atoi(s)
			if err != nil {
				fmt.Println(err)
			} else {
				devInfo.FrameTimerout = atoi
			}
			break
		case 9: //ip地址
			devInfo.Address = s
			break
		case 10: //端口号
			//atoi, err := strconv.Atoi(s)
			//if err != nil {
			//	fmt.Println(err)
			//} else {
			//	devInfo.Port = atoi
			//}
			devInfo.Port = s

			break
		case 11:
			devInfo.ProtocolType = s
			break
		case 12:
			devInfo.DevName = s
			break
		case 13:
			atoi, err := strconv.Atoi(s)
			if err != nil {
				fmt.Println(err)
			} else {
				devInfo.DevTimeout = atoi
			}
		case 14:
			atoi, err := strconv.Atoi(s)
			if err != nil {
				fmt.Println(err)
			} else {
				devInfo.DevId = atoi
			}
		case 15:
			devInfo.DevType = s
		}
	}
	return devInfo
}

type DevRegister struct {
	Functioncode int           `json:"function_code"`
	RegAddr      int           `json:"reg_addr"`
	DataType     string        `json:"data_type"`
	BitIndex     int           `json:"bit_index"`
	Key          string        `json:"key"`
	Expre        string        `json:"expre"`
	RegName      string        `json:"reg_name"`
	AlarmFlag    int           `json:"alarm_flag"`
	Ratio        float32       `json:"ratio"`
	Unit         string        `json:"unit"`
	Priority     int           `json:"priority"`
	Period       int           `json:"period"`
	BigLittle    int           `json:"big_little"`
	ListTable    []DevRegister `json:"list"`
}

type DataVal struct {
	TimeStamp string  `json:"date,omitempty"`
	Name      string  `json:"name"`
	Key       string  `json:"key"`
	Val       float32 `json:"val"`
	RegAddr   int     `json:"reg_addr"`
}

type ReportDataMsg struct {
	DevName   string    `json:"dev_name"`
	DevType   string    `json:"dev_type"`
	ParaType  string    `json:"para_type"`
	TimeStamp time.Time `json:"time_stamp"`
	Data      []DataVal `json:"data"`
}

func NewDevTable() DevInfoTable {
	table := DevInfoTable{}
	return table
}

func DevRegAnalysis_strings(info []string) DevRegister {
	register := DevRegister{}
	//register.ListTable = make([]DevRegister, 0)
	for i, s := range info {
		switch i {
		case 0:
			atoi, err := strconv.Atoi(s)
			if err != nil {

			} else {
				register.Functioncode = atoi
			}
			break
		case 1:
			upper := strings.ToUpper(s)
			contains := strings.Contains(upper, "0X")
			if contains == true {
				all := strings.ReplaceAll(upper, "0X", "")
				parseInt, _ := strconv.ParseInt(all, 16, 32)
				register.RegAddr = int(parseInt)
			} else {
				parseInt, _ := strconv.ParseInt(upper, 10, 32)
				register.RegAddr = int(parseInt)
			}
		case 2:
			register.DataType = s
		case 3:
			atoi, err := strconv.Atoi(s)
			if err != nil {
				register.BitIndex = -1
			} else {
				register.BitIndex = atoi
			}
		case 4:
			register.Key = s
		case 5:
			register.Expre = s
		case 6:
			register.RegName = s
		case 7:
			atoi, err := strconv.Atoi(s)
			if err != nil {

			} else {
				register.AlarmFlag = atoi
			}
		case 8:
			atoi, err := strconv.ParseFloat(s, 32)
			if err != nil {

			} else {
				radio := math.Round(atoi*1000) / 1000
				register.Ratio = float32(radio)
				//register.Ratio = atoi
			}
		case 9:
			register.Unit = s
		case 10:
			atoi, err := strconv.Atoi(s)
			if err != nil {

			} else {
				register.Priority = atoi
			}

		case 11:
			atoi, err := strconv.Atoi(s)
			if err != nil {

			} else {
				register.Period = atoi
			}

		case 12:
			upper := strings.ToUpper(s)
			all := strings.ReplaceAll(upper, "0X", "")
			switch all {
			case "BIG":
				register.BigLittle = BIG_ENDIAN
			case "LITTLE":
				register.BigLittle = LITTLE_ENDIAN
			}
		}
	}
	return register
}

type ModbusRegTable struct {
	DiscreteInputs   []byte
	Coils            []byte
	HoldingRegisters []uint16
	InputRegisters   []uint16
}

type ModbusRegStateTable struct {
	LockData         sync.RWMutex
	DiscreteInputs   []byte
	DiscreteState    []byte
	Coils            []byte
	CoilsState       []byte
	HoldingRegisters []uint16
	HoldingState     []byte
	InputRegisters   []uint16
	InputState       []byte
	Timestamp        time.Time
}

type RegInfoTable struct {
	CoisReg      map[int]DevRegister
	HoldingReg   map[int]DevRegister
	InputReg     map[int]DevRegister
	DiscretInput map[int]DevRegister
}

type DevInfoTable struct {
	DevInfo          DevInfo
	RegTable         RegInfoTable
	RegBufTable      ModbusRegTable
	RegBufStateTable ModbusRegStateTable
	Collect          []Collect_table
}

type DevInfoMap struct {
	Mapreg map[int][]DevRegister
}

func shell_regSourt(register []DevRegister) {
	for gap := len(register); gap > 0; gap /= 2 {
		for i := gap; i < len(register); i++ {
			devRegister := register[i]
			j := i - gap
			for j > 0 && devRegister.RegAddr < register[j].RegAddr {
				register[j+gap] = register[j]
				j -= gap
			}
			register[j+gap] = devRegister
		}
	}
}

func Bubble_sort(register []DevRegister) []DevRegister {
	n := len(register)
	for i := 0; i < n-1; i++ {
		exchange := false
		for j := 0; j < n-i-1; j++ {
			if register[j].RegAddr > register[j+1].RegAddr {
				register[j], register[j+1] = register[j+1], register[j]
				exchange = true
			}
		}
		if !exchange {
			return register
		}
	}
	return register
}

type Collect_table struct {
	FunctionCode int `json:"function_code"`
	StartAddr    int `json:"start_addr"`
	Length       int `json:"length"`
	Period       int `json:"period"`
}

func Collect_Analysis_strings(info []string) Collect_table {
	table := Collect_table{}
	for i, s := range info {
		switch i {
		case 0:
			atoi, err := strconv.Atoi(s)
			if err != nil {

			} else {
				table.FunctionCode = atoi
			}
			break
		case 1:
			upper := strings.ToUpper(s)
			contains := strings.Contains(upper, "0X")
			if contains == true {
				all := strings.ReplaceAll(upper, "0X", "")
				parseInt, _ := strconv.ParseInt(all, 16, 32)
				table.StartAddr = int(parseInt)
			} else {
				parseInt, _ := strconv.ParseInt(upper, 10, 32)
				table.StartAddr = int(parseInt)
			}
			//atoi, err := strconv.Atoi(s)
			//if err != nil {
			//
			//} else {
			//	table.StartAddr = atoi
			//}
			break
		case 2:
			atoi, err := strconv.Atoi(s)
			if err != nil {

			} else {
				table.Length = atoi
			}
			break
		case 3:
			atoi, err := strconv.Atoi(s)
			if err != nil {

			} else {
				table.Period = atoi
			}
			break
		}
	}
	return table
}
